package cn.infrabase.core.text;

import cn.infrabase.System;
import cn.infrabase.core.lang.ArrayUtils;
import cn.infrabase.core.lang.Filter;
import cn.infrabase.core.util.CharUtils;
import cn.infrabase.core.util.CharsetUtils;

import javax.annotation.Nullable;
import java.nio.charset.Charset;

/**
 * 字符序列工具类。
 *
 * @author Weimin Gao
 * @since 1.0.0
 **/
public class CharSequenceUtils {

	/**
	 * <pre>
	 * 断言给定的字符序列是否为null或空字符序列或空白字符序列。
	 * 例子：
	 * 1、assertTrue(CharSequenceUtils.isBlank(null));
	 * 2、assertTrue(CharSequenceUtils.isBlank(""));
	 * 3、assertTrue(CharSequenceUtils.isBlank(" "));
	 * 4、assertTrue(CharSequenceUtils.isBlank("  "));
	 * 5、assertTrue(CharSequenceUtils.isBlank("\f"));
	 * 6、assertTrue(CharSequenceUtils.isBlank("\t"));
	 * 7、assertTrue(CharSequenceUtils.isBlank("\n"));
	 * 8、assertTrue(CharSequenceUtils.isBlank("\r"));
	 * 9、assertTrue(CharSequenceUtils.isBlank("\n\r"));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 真（true）表示为null或空字符序列或空白字符序列。
	 */
	public static boolean isBlank(CharSequence cs) {
		if (cs != null) {
			int length = cs.length();
			for (int i = 0; i < length; i++) {
				if (!CharUtils.isBlankChar(cs.charAt(i))) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * <pre>
	 * 断言给定的字符序列不为null和空白字符，此方法与{@link #hasText}相同。
	 * 例子：
	 * 1、assertFalse(CharSequenceUtils.isNotBlank(null));
	 * 2、assertFalse(CharSequenceUtils.isNotBlank(""));
	 * 3、assertFalse(CharSequenceUtils.isNotBlank(" "));
	 * 4、assertFalse(CharSequenceUtils.isNotBlank("  "));
	 * 5、assertFalse(CharSequenceUtils.isNotBlank("\f"));
	 * 6、assertFalse(CharSequenceUtils.isNotBlank("\t"));
	 * 7、assertFalse(CharSequenceUtils.isNotBlank("\n"));
	 * 8、assertFalse(CharSequenceUtils.isNotBlank("\r"));
	 * 9、assertFalse(CharSequenceUtils.isNotBlank("\n\r"));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 真（true）表示给定的字符序列不为null和空白字符。
	 */
	public static boolean isNotBlank(CharSequence cs) {
		return !isBlank(cs);
	}

	/**
	 * <pre>
	 * 断言给定的字符序列不为null和空白字符，此方法与{@link #isNotBlank}相同。
	 * 例子：
	 * 1、assertFalse(StringUtils.hasText(null));
	 * 2、assertFalse(StringUtils.hasText(""));
	 * 3、assertFalse(StringUtils.hasText(" "));
	 * 4、assertFalse(StringUtils.hasText("  "));
	 * 5、assertFalse(StringUtils.hasText("\f"));
	 * 6、assertFalse(StringUtils.hasText("\t"));
	 * 7、assertFalse(StringUtils.hasText("\n"));
	 * 8、assertFalse(StringUtils.hasText("\r"));
	 * 9、assertFalse(StringUtils.hasText("\n\r"));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 真（true）表示给定的字符序列不为null和空白字符。
	 */
	public static boolean hasText(CharSequence cs) {
		return !isBlank(cs);
	}

	/**
	 * <pre>
	 * 断言给定的字符序列数组中是否包含空字符序列。
	 * 说明：
	 * 1、如果给定的字符序列数组的长度为0，或者其中的任意一个元素是字符序列，则返回真（true）。
	 * 例子：
	 * 1、assertTrue(CharSequenceUtils.hasBlank());
	 * 2、assertTrue(CharSequenceUtils.hasBlank("", null, " "));
	 * 3、assertTrue(CharSequenceUtils.hasBlank("123", " "));
	 * </pre>
	 *
	 * @param css 给定的字符序列数组。
	 * @return 真（true）表示给定的字符序列数组中包含空字符。
	 */
	public static boolean hasBlank(CharSequence... css) {
		if (ArrayUtils.isEmpty(css)) {
			return true;
		}
		for (CharSequence cs : css) {
			if (isBlank(cs)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * <pre>
	 * 断言给定的字符序列数组中是否全部为空字符序列。
	 * 说明：
	 * 1、如果给定的字符序列数组的长度为0，或者所有元素都是空字符序列，则返回真（true）。
	 * 例子：
	 * 1、assertTrue(CharSequenceUtils.isAllBlank());
	 * 2、assertTrue(CharSequenceUtils.isAllBlank("", null, " "));
	 * 3、assertFalse(CharSequenceUtils.isAllBlank("123", " "));
	 * </pre>
	 *
	 * @param css 给定的字符序列数组。
	 * @return 真（true）表示给定的字符序列数组全部为空字符序列。
	 */
	public static boolean isAllBlank(CharSequence... css) {
		if (ArrayUtils.isEmpty(css)) {
			return true;
		}
		for (CharSequence cs : css) {
			if (isNotBlank(cs)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * <pre>
	 * 断言给定的字符序列是否为空。
	 * 说明：
	 * 1、如果给定的字符序列为null或长度为0时，则返回真（true）。
	 * 例子：
	 * 1、assertTrue(CharSequenceUtils.isEmpty(null));
	 * 2、assertTrue(CharSequenceUtils.isEmpty(""));
	 * 3、assertFalse(CharSequenceUtils.isEmpty(" "));
	 * 4、assertFalse(CharSequenceUtils.isEmpty("\t"));
	 * 5、assertFalse(CharSequenceUtils.isEmpty("\n"));
	 * 6、assertFalse(CharSequenceUtils.isEmpty("\r"));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 真（true）表示给定的字符序列为null或空。
	 */
	public static boolean isEmpty(CharSequence cs) {
		return cs == null || cs.length() == 0;
	}

	/**
	 * <pre>
	 * 断言给定的字符序列是否不为空。
	 * 说明：
	 * 1、如果给定的字符序列不为null且长度不为0时，则返回真（true）。
	 * 例子：
	 * 1、assertFalse(CharSequenceUtils.isNotEmpty(null));
	 * 2、assertFalse(CharSequenceUtils.isNotEmpty(""));
	 * 3、assertTrue(CharSequenceUtils.isNotEmpty(" "));
	 * 4、assertTrue(CharSequenceUtils.isNotEmpty("\t"));
	 * 5、assertTrue(CharSequenceUtils.isNotEmpty("\n"));
	 * 6、assertTrue(CharSequenceUtils.isNotEmpty("\r"));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 真（true）表示给定的字符序列不为null和空。
	 */
	public static boolean isNotEmpty(CharSequence cs) {
		return !isEmpty(cs);
	}

	/**
	 * <pre>
	 * 将给定的字符序列转换为字符串。
	 * 例子：
	 * 1、assertNull(CharSequenceUtils.toString(null));
	 * 2、assertEquals("",CharSequenceUtils.toString(""));
	 * 3、assertEquals("123",CharSequenceUtils.toString("123"));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 字符串。
	 */
	public static String toString(CharSequence cs) {
		return null == cs ? null : cs.toString();
	}

	/**
	 * <pre>
	 * 将给定的字符序列根据指定的字符过滤器过滤对应的字符，返回过滤后的字符序列。
	 * </pre>
	 *
	 * @param cs     给定的字符序列。
	 * @param filter 指定的字符过滤器，过滤{@link Filter#accept(Object)}返回为{@code true}的字符。
	 * @return 过滤后的字符序列。
	 */
	public static CharSequence filter(CharSequence cs, final Filter<Character> filter) {
		if (cs == null || filter == null) {
			return toString(cs);
		}
		int len = cs.length();
		final StringBuilder sb = new StringBuilder(len);
		char c;
		for (int i = 0; i < len; i++) {
			c = cs.charAt(i);
			if (filter.accept(c)) {
				sb.append(c);
			}
		}
		return sb.toString();
	}

	/**
	 * <pre>
	 * 清理给定字符序列中包含的空白字符，返回清理后的字符序列。
	 * 例子：
	 * 1、assertNull(CharSequenceUtils.cleanBlank( null));
	 * 2、assertEquals("", CharSequenceUtils.cleanBlank( ""));
	 * 3、assertEquals("", CharSequenceUtils.cleanBlank( " "));
	 * 4、assertEquals("", CharSequenceUtils.cleanBlank( "  "));
	 * 5、assertEquals("返回值", CharSequenceUtils.cleanBlank( " 返回值 "));
	 * 6、assertEquals("第一行第二行", CharSequenceUtils.cleanBlank( "第一行\n\r第二行\t"));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 清理后的字符序列。
	 */
	public static CharSequence cleanBlank(CharSequence cs) {
		return filter(cs, c -> !CharUtils.isBlankChar(c));
	}


	// -----------------------------------------------------------------------------------------------------------------
	// bytes

	/**
	 * <pre>
	 * 将给定的字符序列使用指定的字符集进行编码返回一个字节数组。
	 * 说明：
	 * 1、如果给定的字符序列为null时，则返回null。
	 * 2、如果指定的字符集为null时，则使用使用平台默认的字符集。
	 * 例子：
	 * 1、assertNull(CharSequenceUtils.bytes(null, null));
	 * 2、assertEquals("[97, 98, 99]", ArrayUtils.toString(CharSequenceUtils.bytes("abc")));
	 * </pre>
	 *
	 * @param cs      给定的字符序列。
	 * @param charset 指定的字符集。
	 * @return 编码后的字节数组。
	 */
	public static byte[] bytes(@Nullable CharSequence cs, @Nullable Charset charset) {
		if (cs == null) {
			return null;
		}
		if (null == charset) {
			return cs.toString().getBytes();
		}
		return cs.toString().getBytes(charset);
	}

	/**
	 * <pre>
	 * 将给定的字符序列使用指定的字符集进行编码返回一个字节数组。
	 * 说明：
	 * 1、如果给定的字符序列为null时，则返回null。
	 * 2、如果指定的字符集为null时，则使用使用平台默认的字符集。
	 * 例子：
	 * 1、assertNull(CharSequenceUtils.bytes(null, null));
	 * 2、assertEquals("[97, 98, 99]", ArrayUtils.toString(CharSequenceUtils.bytes("abc")));
	 * </pre>
	 *
	 * @param cs      给定的字符序列。
	 * @param charset 指定的字符集。
	 * @return 编码后的字节数组。
	 */
	public static byte[] bytes(CharSequence cs, String charset) {
		return bytes(cs, isBlank(charset) ? System.defaultCharset() : Charset.forName(charset));
	}

	/**
	 * <pre>
	 * 将给定的字符序列使用平台默认的字符集进行编码返回一个字节数组。
	 * 说明：
	 * 1、如果给定的字符序列为null时，则返回null。
	 * 例子：
	 * 1、assertNull(CharSequenceUtils.bytes(null, null));
	 * 2、assertEquals("[97, 98, 99]", ArrayUtils.toString(CharSequenceUtils.bytes("abc")));
	 * </pre>
	 *
	 * @param cs 给定的字符序列。
	 * @return 编码后的字节码。
	 */
	public static byte[] bytes(CharSequence cs) {
		return bytes(cs, System.defaultCharset());
	}

	/**
	 * 将给定的字符串使用utf8字符集进行编码，返回编码后的字节数组。
	 *
	 * @param cs 给定的字符串。
	 * @return 编码后的字节数组。
	 */
	public static byte[] utf8Bytes(CharSequence cs) {
		return bytes(cs, CharsetUtils.CHARSET_UTF_8);
	}

}
